#include <pthread.h>
#include <signal.h>

#include "register.h"
#include "../libreria.h"

#define QUEUE_SIZE 5


Register registro; 	// registro usato per memorizzare le registrazioni (e deregistrazioni) dei vari Service Provider
		   	// e gestire le interrogazioni dei vari client

pthread_mutex_t sem;	// semaforo di mutua esclusione per l'accesso e la modifica del registro tra i vari thread che gestiscono le richieste

int s_listening;	// socket di listening




void deregistra(int sig_receive)
{
	close(s_listening);
}

// Thread che gestisce le richieste
void* gestisci_richiesta(void *From)
	
{
	
	host mitt;
	int i;


	mitt = *(host *)(From);		// mitt vanno i dati relativi al socket del client (può anche essere un Service Provider) 
					// che ha fatto una richiesta
	char buffer [BUFFER_SIZE];	// buffer dove viene ricevuta il messaggio di richiesta	
	host service_provider;		// nella struttura service_provider ci sono ip e porta del service provider memorizzati (o da memorizzare)
					// nel registro e passati dentro il messaggio
	char nome_servizio [BUFFER_SIZE];// nome del servizio presente nel messaggio

	memset(buffer,0,BUFFER_SIZE);
	//memset(service_provider,0,sizeof(service_provider));	

		// Ricevo la richiesta del client (o Service Provider) fino al carattere # considerato di fine richiesta
	i=0;
	do
		recv (mitt.s,&buffer[i],1,0);
	while (buffer[i++]!='#' && i<BUFFER_SIZE);
	
	
	switch (buffer[0])
	{
	case '0': 
		{
	
		//Service Provider fa una richiesta di registrazione di un servizio
		
		char porta[BUFFER_SIZE];		// numero di porta usata dal Service Provider per ricevere richieste per quel servizio
		
		printf("SERVICE PROVIDER richiede registrazione\n");
		
		int num_service;
		
		num_service=decode_msg_from_SP(buffer,porta,service_provider.ip);
	
		if(num_service<0)
		{	
			printf("Ricevuta richiesta di registrazione di servizio con parametri errati\n");
			break;
		}
		service_provider.porta=atoi(porta); //viene registrata la porta inviata nel messaggio

		for (int i=0; i< num_service; i++)
		{			
			receive_name_service(nome_servizio,mitt.s);

			printf("SERVICE PROVIDER (%s:%d)  ha registrato servizio %s\n",service_provider.ip,service_provider.porta,nome_servizio); 
				

			//Richiesta deregistrazione service provider dal registro

			pthread_mutex_lock(&sem);
			registro.add_node(nome_servizio,service_provider);
			pthread_mutex_unlock(&sem);


		
		}

		}
		break;

	case '1':
		{ 
		//service provider fa una richiesta di deregistrazione

		

		char porta[BUFFER_SIZE];
		bool b;
		int num_service;
		
		num_service=decode_msg_from_SP(buffer,porta,service_provider.ip);
	
		if(num_service<=0)
		{	
			printf("Ricevuta richiesta di deregistrazione di servizio con parametri errati\n");
			break;
		}
		service_provider.porta=atoi(porta); //viene registrata la porta inviata nel messaggio

		
		for (int i=0; i< num_service; i++)
		{			
			receive_name_service(nome_servizio,mitt.s);

			printf("SERVICE PROVIDER (%s:%d) richiede deregistrazione di %s.\n",service_provider.ip,service_provider.porta,nome_servizio); 
				

			//Richiesta deregistrazione service provider dal registro

			pthread_mutex_lock(&sem);
			b=registro.rm_service_provider(service_provider.ip,service_provider.porta, nome_servizio);
			pthread_mutex_unlock(&sem);

			if(!b)
				printf("Servizio non presente\n");
			else
				printf ("Servizio deregistrato\n");
		}

		}
		break;
	
	case '2':
		{ 
		//client che fa rischiesta di indirizzo Ip e Porta di un servizio			
		
		host *p;

		printf("CLIENT (%s) connesso\n",mitt.ip);
		
		if(!decode_msg_from_client(buffer,nome_servizio))
		{
			printf("Ricevuta interrogazione dal client con parametri errati\n");
			break;
		}

		printf("CLIENT (%s) ha fatto richiesta di servizio %s\n",mitt.ip,nome_servizio);
		
		//Ricerca del servizio all'interno del registro
		pthread_mutex_lock(&sem);
		p = registro.extract_node(nome_servizio);
		pthread_mutex_unlock(&sem);
		
		if (p == NULL)
			{
			//Servizio non presente nel registro
			printf("Il servizio cercato dal client non è disponibile\n");
			send(mitt.s,"0;",2,0);
			}
		else	
			{
			// Servizio presente nel registro
			// Invio IP e porta al client del service provider che esegue quel servizio
			
			printf("Il servizio si trova a %s e porta %i\n",p->ip,p->porta);
			sprintf(buffer,"1;%s;%d;",p->ip,p->porta);	
		
			if(send(mitt.s,buffer,strlen(buffer),0) == -1)
				printf("Errore invio intestazione crittogramma\n");
			}
		}
		break;
	
	}

	close(mitt.s);

	return NULL;
}


int main (int argc, char * argv[])
{
	struct sockaddr_in client_addr;
	
	host client;
	

	int s_accept;		// socket di connessione
	socklen_t len = sizeof(struct sock_addr*);//lunghezza in byte della struttura sock_addr
	int local_port_number;	//porta locale Server
	pthread_t tid;		// identificatore thread
	
	signal(SIGINT,deregistra);

	
	//////////    CONTROLLO PARAMETRI DA RIGA DI COMANDO   //////////
	if (argc != 3)
	{
		printf("Errore numero parametri:\nUsage: ./ServiceRegister ip_service_register porta_service_register\n");
		exit(1);
	}
	
	printf("SERVICE REGISTER, v.1.0\n");


	local_port_number = atoi(argv[2]);//porta server passata da parametro
	
	
	//////////    INIZIALIZZAZIONE SOCKET DI LISTENING   ///////////////

	s_listening=inizializza_server(argv[1], local_port_number, QUEUE_SIZE);
	

	printf ("Attesa di connessioni...\n");

	pthread_mutex_init(&sem,0); //inizializziamo semaforo di mutua esclusione

	while (1)
	{	
		s_accept = accept(s_listening,(struct sockaddr*) &client_addr, &len);//creo il socket di connnessione
		
		if (s_accept == (-1))
		{ 
			printf ("ERRORE nella connessione!\n");
			exit(1);
		}
		
		
		inet_ntop(AF_INET,&client_addr.sin_addr,client.ip,sizeof(client.ip));
		client.s = s_accept;
		client.porta = ntohs(client_addr.sin_port);

		if (pthread_create (&tid, NULL, gestisci_richiesta,(void *)&client)!=0) 
		{
			printf("Errore nella creazione del Thread...\n");
			exit(1);
		}	
	}
}

